#include <stdio.h>
#include <sys/types.h>          /* See NOTES */
#include <sys/socket.h>
#include <string.h>
#include <errno.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <netinet/in.h>
#include <getopt.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <time.h>
#include <pthread.h>


#define	BACKLOG	 13
#define BUF_SIZE 1024

#define	MSG_STR	"Hello Client Welcome to Connect...!"


char cover(char ch) ;				//change char to upper
void *thread_work( void *ctx) ;			//thread_function

void print_usage(const char *program_name) 
{  
		printf("\n%s -- (2018.11.20)\n", program_name);  
        	printf(" Usage: %s -p <server_port>  [-h <server_use_help>]\n", program_name);  
		printf("	-p --port	the server listen port\n") ;
		printf("	-h --help	the server file how to use\n");  

		return ;
}  






int main( int argc, char **argv )
{
	int listen_fd,client_fd = -1 ;
	struct sockaddr_in	serv_addr ;
	struct	sockaddr_in	cli_addr ;
	socklen_t		cli_addr_len ;
	int			opt = -1 ;	
	int			port = 0 ;
	pthread_t		tid ;
	pthread_attr_t		thread_attr ;
	time_t 			timep;   
	struct tm 		*tim; 
	int 			log ; // open server.log to record 
	char			log_name[32] = "thread_server.log" ;
	struct stat		info_file ;	 //get file size, if too big, will lseek to SEEK_SET



	const char *short_opts = "p:h";  			//set  option


	const struct option long_opts[] = 	{  

						{"help", no_argument, NULL, 'h'},  

				     		{ "port", required_argument, NULL, 'p'},  

					       {0, 0, 0, 0}  
			        	};  


	while ((opt= getopt_long(argc, argv, short_opts, long_opts,NULL)) != -1) 
	{
		switch (opt) 
		{
			case 'p':
				port = atoi(optarg);
				break ;

			case 'h':
				print_usage(argv[0]) ;
				return 0;
		
		
		}			
	

	}

	if( !port )
	{
		print_usage(argv[0]);
		return 0;
	
	}



	log = open(log_name, O_RDWR|O_CREAT|O_APPEND, 0666) ;          
	if(log < 0 )
	{
		printf("open log_file failure:%s\n", strerror(errno)) ;
		return  0 ;
	}
	if ( daemon(1,1) < 0 )  						//set program  in backgrund 
	{
		printf("daemon failure: %s\n", strerror(errno)) ;
		goto cleanup ;
	}

	if( dup2(log, STDOUT_FILENO) < 0 ) 					//program running record in "thread_server.log"
	{
		printf("dup2 failure: %s\n", strerror(errno)) ;
		goto cleanup ;
	}



/*			Listen and Bind					*/
	listen_fd = socket(AF_INET, SOCK_STREAM, 0) ;
	if(listen_fd < 0)
	{
		printf("creat socket failure : %s \n", strerror(errno) ) ;
		return -2 ;
	
	}
	printf("creat socket suceeful, listen_fd descriptor[%d]\n", listen_fd) ;

	memset(&serv_addr, 0, sizeof(serv_addr));
	serv_addr.sin_family = AF_INET ;
	serv_addr.sin_port = htons(port) ;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY) ;
	memset(&cli_addr, 0, sizeof(serv_addr));

	 int reuse = 1 ;
	 setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)); //	Reuseaddr 

	if ( bind(listen_fd, (struct sockaddr*)&serv_addr, sizeof(serv_addr) ) < 0 )
	{
		printf("socket bind failure : %s\n", strerror(errno) ) ;
		goto cleanup ;
	}
	

	if ( listen(listen_fd ,BACKLOG) < 0 )
	{	
		printf("socket listen failure: %s\n", strerror(errno) ) ;
		goto cleanup ;
	}

	/*			Accpect and ask client			*/
	while(1)
	{
		printf("\n/********************************************************************/\n") ;	
		localtime(&timep) ;
		time(&timep);
		tim = localtime(&timep);
		printf("Server Start Work, Server port:%d, And Waiting The Client Connect ...\n", port) ;
		/*	accpect	client	*/	
		client_fd = accept(listen_fd,(struct sockaddr*) &cli_addr, &cli_addr_len) ; 
		if(client_fd < 0)
		{
			printf("accept failure: %s\n", strerror(errno)) ;
			return -3 ;
		}
		printf("accept succeful, client [%s:%d] \n", inet_ntoa(cli_addr.sin_addr), ntohs(cli_addr.sin_port) ) ;
		printf("\n			%d年%d月%d日 %d:%d:%d\n", (1900 + tim->tm_year), ( 1 + tim->tm_mon), tim->tm_mday, tim->tm_hour, tim->tm_min, tim->tm_sec); 
	

		/*	create thread to ask client	*/

		if( pthread_attr_init(&thread_attr) ) //On success, these functions return 0; on error, they return a nonzero error number.
		{
			printf("pthread_attr_init failure: %s\n", strerror(errno) ) ;
			return -4 ;
		}

		if( pthread_attr_setstacksize( &thread_attr, 120*1024) ) //int pthread_attr_setstackaddr(pthread_attr_t *attr, void *stackaddr);success,functions return 0; 
		{
			printf("pthread_attr_setstacksize failure: %s\n", strerror(errno) ) ;
			return -5 ;
		}
		
		if( pthread_attr_setdetachstate( &thread_attr, PTHREAD_CREATE_DETACHED )) 
		/* int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate); detach: PTHREAD_CREATE_DETACHED, PTHREAD_CREATE_JOINABLE, success return 0; */
		{	
			printf("pthread_attr_setdetachstate failure: %s\n", strerror(errno)) ;
			return -6 ;
		}
		
		/*	Get log size	*/
		if(stat(log_name, &info_file) < 0 )
		{
			printf("Stat failed: %s\n", strerror(errno)) ;
			goto cleanup ;
		}
		printf("\n################# %s size: %ld Bytes #################\n",log_name, info_file.st_size) ;


		if(  pthread_create( &tid, &thread_attr, thread_work,(void *)client_fd) )      //create thread
		{
			printf("pthread_create failure: %s\n", strerror(errno)) ;
			return -7 ;
		}


	}

cleanup:
	close(log) ;
	close(listen_fd) ;
	close(client_fd) ;
	



	return 0 ;
}


void *thread_work( void *ctx )
{	
	int client_fd ;
	int rv = -1 ;
	int i ;
	char buf[BUF_SIZE] ;
	char wbuf[BUF_SIZE] = "Hello client, Welcome to connect, If you input 'quit' you will exit,If you input get_time will get server time , If you input other  will echo upper buf" ;
	time_t ticks ;

	if( !ctx ) 
	{
		printf("Invalid input arguments in %s()\n", __FUNCTION__) ; // _FUNCTION get the function name
		pthread_exit(NULL) ;
	}


	client_fd = (int)ctx ;
	if (write( client_fd, wbuf, strlen(wbuf) ) < 0 )
	{
		printf("write to client[%d] failure: %s and thread will exit!\n", client_fd, strerror(errno) ) ;
		close(client_fd) ;
		pthread_exit(NULL) ;
	}


	while(1)
	{
		memset(buf,0,sizeof(buf));
		rv = read( client_fd, buf, sizeof(buf) ) ;

		if(rv < 0 )
		{
			printf("read to client[%d] failure: %s and thread will exit!\n", client_fd, strerror(errno) ) ;
			close(client_fd) ;
			pthread_exit(NULL) ;
		}	

		if( rv == 0 )
		{
			printf("client[%d] disconnect and thread will exit!\n", client_fd) ;
			close(client_fd) ;
			pthread_exit(NULL) ;
		}	
	
		printf("read %d byte from client[%d]: %s\n", rv, client_fd, buf) ;
		fflush(stdout) ;

		
		bzero(wbuf,sizeof(wbuf)) ;

		if( strcmp(buf, "quit") == 0)
		{
			if (write( client_fd, "You will exit,Good bye!", strlen("You will exit") ) < 0 )
			{
				printf("write to client[%d] failure: %s and thread will exit!\n", client_fd, strerror(errno) ) ;
				close(client_fd) ;
				pthread_exit(NULL) ;
			}
				
				sleep(1) ;								//client send quit close(client_fd) ;
				close(client_fd) ;
				pthread_exit(NULL) ;


		}
		else if( strcmp(buf, "get_time") == 0)		//send time to client
		{
			ticks = time(NULL) ;
			snprintf( wbuf, sizeof(wbuf), "%.24s",ctime(&ticks)) ;
			if(write( client_fd, wbuf, strlen(wbuf)) <0 )
			{
				printf("write to client[%d] failure: %s and thread will exit!\n", client_fd, strerror(errno)) ;
				close(client_fd) ;
				pthread_exit(NULL) ;
			}
		}
		else					//echo buf to client
		{
			for(i=0 ; i < rv; i++ )
			{	
				buf[i] = cover(buf[i]) ; 
			}
	
			if (write( client_fd, buf, strlen(buf) ) < 0 )
			{
				printf("write to client[%d] failure: %s and thread will exit!\n", client_fd, strerror(errno) ) ;
				close(client_fd) ;
				pthread_exit(NULL) ;
			}
	
		}
	}

}


char cover(char ch)
{
	if(ch>'z'||ch<'a')
	{
		return ch ;		
	}
	else
		return (ch - ('a' - 'A') ); 
}
